#include <stdio.h>
#include <sys/types.h>		//	open 依赖头文件
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>         //  close 依赖头文件
#include <stdlib.h>         //  malloc abs 依赖头文件
#include "my_codeh/pic.h"            //  自定义图片 头文件
#include "my_codeh/lcd.h"          //  自定义显示 头文件


/*
    get_pixel_array:    获取 BMP 图片像素数组
    @bmpfile:           待获取的 BMP 路径名
    retval:             成功返回 指向的空间保存了图片信息
                        失败返回 NULL
*/
BMP *get_bmp_info(char *bmpfile)
{
    BMP *bmp = malloc(sizeof(BMP));
    //  1、打开 BMP 图片
    int fd = open(bmpfile,O_RDONLY);    //  只读打开 BMP 图片
    if(fd == -1)                        //  打开错误
    {
        perror("open bmp error");       //  解析错误
        return NULL;
    }

    //  2、判断是不是 BMP
    char flag[2] = {0};                 //  保存图片的标志
	read(fd,flag,2);
    if(!(flag[0] == 'B' && flag[1] == 'M'))
    {
        printf("The file isn't BMP\n");
        close(fd);
        free(bmp);
        return NULL;
    }

    //  3、获取相关信息	width height depth      
    //  3,1 解析 bmp 宽度:
    lseek(fd,0x12,SEEK_SET);		    //	先找到对应位置
    read(fd,&bmp->width,4);
	//  3.2 解析 bmp 高度:
    int height; 					    //	(机器是小端)
    lseek(fd,0x16,SEEK_SET);		    //	先找到对应位置
    read(fd,&bmp->height,4);
    //  3.3 解析 bmp 色深:
    short depth; 					    //	(机器是小端)
    lseek(fd,0x1C,SEEK_SET);		    //	先找到对应位置
    read(fd,&bmp->depth,2);
    printf("%s:%d-%d-%hd\n",bmpfile,bmp->width,bmp->height,bmp->depth);

    //  4、获取像素数组
    // 一行实际字节数
    int line_valid_bytes = abs(bmp->width) * bmp->depth / 8; 	
    // 一行存储字节数 = 一行实际字节数 + 若干个空白的数据
    int line_bytes; 
    // 若干个空白数据
    bmp->add = (line_valid_bytes % 4)?(4-line_valid_bytes%4):0; 
    line_bytes = line_valid_bytes + bmp->add;
    // 实际像素数组的大小
    int size = line_bytes * abs(bmp->height); 		
    //  获取像素数组:
    bmp->pixel_array = malloc(size);		//	malloc 分配空间
    lseek(fd,0x36,SEEK_SET);			    //	先找到对应位置
    read(fd,bmp->pixel_array,size);			//	将数据全部读取出来  

    //  5、关闭 BMP 图片
    close(fd);

    return bmp;
}

/*
    free_bmp_info:  释放BMP信息空间
    @bmp:           指向的空间保存了 BMP信息空间地址
*/
void free_bmp_info(BMP **bmp)
{
    free((*bmp)->pixel_array);      //  释放像素数组空间
    (*bmp)->pixel_array = NULL;     //  防止内存泄漏
    free(*bmp);                     //  释放对应 信息空间
    *bmp = NULL;                    //  防止内存泄漏
}

/*
    display_bmp_reg:    正常显示 BMP 图片
    @x0:                待显示 BMP 图片在坐标轴的 X 坐标起点
    @y0:                待显示 BMP 图片在坐标轴的 Y 坐标起点
    @bmp:               指向的空间保存了 BMP信息
    @plcd:              指向了 Linux 帧缓冲,保存其首地址
*/
void display_bmp_reg(int x0,int y0,BMP *bmp,unsigned int *plcd)
{
    unsigned char a,r,g,b; 	// 	每个像素点的颜色值分量信息
    unsigned char *pixel = bmp->pixel_array;
    for(int y = 0;y < abs(bmp->height);y++) 		//	一共有多少行像素点,一行一行的显示
    {
        for(int x = 0;x < abs(bmp->width);x++) 	    // 一行有多少个像素点,一行中一个个像素点显示
        {
            // 	依次得到每个像素点颜色值分量信息
            b = *pixel++; 		//	小端模式,先获取蓝色分量
            g = *pixel++;		//	小端模式,再获取绿色分量
            r = *pixel++;		//	小端模式,再获取红色分量
            if(bmp->depth == 32) 	// 	只有色深为 32 才有 a 分量
            {
                a = *pixel++;	//	小端模式,最后获取透明度分量
            }
            else
            {
                a = 0;
            }
            int color = a << 24 | r << 16 | g << 8 | b; 	//	当前像素点的颜色值
            int real_x = bmp->width>0 ? x:abs(bmp->width)-1-x;
            int real_y = bmp->height>0 ? bmp->height-1-y:y;
            display_point(real_x+x0,real_y+y0,color,plcd);
            // 宽度 > 0 左到右保存,< 0 右到左保存
            // 高度 > 0 下到上保存,< 0 上到下保存
            // 从 0 计数
        }
        pixel += bmp->add; 	// 	存储时每一行会填充无效字节,跳过填充的数据,不需要显示
    }
}


/*
    display_bmp_special_1:  特效显示 BMP 图片
    @x0:                    待显示 BMP 图片在坐标轴的 X 坐标起点
    @y0:                    待显示 BMP 图片在坐标轴的 Y 坐标起点
    @bmp:                   指向的空间保存了 BMP信息
    @plcd:                  指向了 Linux 帧缓冲,保存其首地址
*/
void display_bmp_special_1(int x0,int y0,BMP *bmp,unsigned int *plcd)
{
    unsigned char a,r,g,b; 	// 	每个像素点的颜色值分量信息
    int x,y; 				//	像素数组存储的像素点的坐标,不是显示的坐标
    unsigned char *pixel = bmp->pixel_array;
    for(y = 0;y < abs(bmp->height);y++) 		//	一共有多少行像素点,一行一行的显示
    {
        for(x = 0;x < abs(bmp->width);x++) 	    // 一行有多少个像素点,一行中一个个像素点显示
        {
            // 	依次得到每个像素点颜色值分量信息
            b = *pixel++; 		//	小端模式,先获取蓝色分量
            g = *pixel++;		//	小端模式,再获取绿色分量
            r = *pixel++;		//	小端模式,再获取红色分量
            if(bmp->depth == 32) 	// 	只有色深为 32 才有 a 分量
            {
                a = *pixel++;	//	小端模式,最后获取透明度分量
            }
            else
            {
                a = 0;
            }
            int color = a << 24 | r << 16 | g << 8 | b; 	//	当前像素点的颜色值
            int real_x = bmp->width>0 ? x:abs(bmp->width)-1-x;
            int real_y = bmp->height>0 ? bmp->height-1-y:y;
            display_point(real_x+x0,real_y+y0,color,plcd);
            // 宽度 > 0 左到右保存,< 0 右到左保存
            // 高度 > 0 下到上保存,< 0 上到下保存
            // 从 0 计数
        }
        pixel += bmp->add; 	// 	存储时每一行会填充无效字节,跳过填充的数据,不需要显示
        usleep(10 * 1000);  //  每隔 0.01s 显示一行
    }
}


/*
    display_bmp_special_2:  特效显示 BMP 图片
    @x0:                    待显示 BMP 图片在坐标轴的 X 坐标起点
    @y0:                    待显示 BMP 图片在坐标轴的 Y 坐标起点
    @bmp:                   指向的空间保存了 BMP信息
    @plcd:                  指向了 Linux 帧缓冲,保存其首地址
*/
void display_bmp_special_2(int x0,int y0,BMP *bmp,unsigned int *plcd)
{
    //  1、保存所有像素点的颜色值在一个二维数组
    unsigned char a,r,g,b; 	// 	每个像素点的颜色值分量信息
    int w = abs(bmp->width);
    int h = abs(bmp->height);
    int bmp_color[h][w];    //  保存每一个像素点的颜色值
    unsigned char *pixel = bmp->pixel_array;
    for(int y = 0;y < h;y++) 		//	一共有多少行像素点,一行一行的显示
    {
        for(int x = 0;x < w;x++) 	    // 一行有多少个像素点,一行中一个个像素点显示
        {
            // 	依次得到每个像素点颜色值分量信息
            b = *pixel++; 		//	小端模式,先获取蓝色分量
            g = *pixel++;		//	小端模式,再获取绿色分量
            r = *pixel++;		//	小端模式,再获取红色分量
            if(bmp->depth == 32) 	// 	只有色深为 32 才有 a 分量
            {
                a = *pixel++;	//	小端模式,最后获取透明度分量
            }
            else
            {
                a = 0;
            }
            int color = a << 24 | r << 16 | g << 8 | b; 	//	当前像素点的颜色值
            int real_x = bmp->width>0 ? x:abs(bmp->width)-1-x;
            int real_y = bmp->height>0 ? bmp->height-1-y:y;
            bmp_color[real_y][real_x] = color;
            // 宽度 > 0 左到右保存,< 0 右到左保存
            // 高度 > 0 下到上保存,< 0 上到下保存
            // 从 0 计数
        }
        pixel += bmp->add; 	// 	存储时每一行会填充无效字节,跳过填充的数据,不需要显示
    }
    //  2、想怎么显示就怎么显示(百叶窗特效显示)
    for(int y = 0;y < h/4;y++) 		//	一共有多少行像素点,一行一行的显示
    {
        for(int x = 0;x < w;x++) 	    // 一行有多少个像素点,一行中一个个像素点显示
        {
            display_point(x+x0,y+y0,bmp_color[y][x],plcd);
            display_point(x+x0,h/4*1+y+y0,bmp_color[h/4*1+y][x],plcd);
            display_point(x+x0,h/4*2+y+y0,bmp_color[h/4*2+y][x],plcd);
            display_point(x+x0,h/4*3+y+y0,bmp_color[h/4*3+y][x],plcd);
        }
        usleep(50 * 1000);  //  每隔 0.05s 显示 4 行
    }
}

